package io.renren.membermanage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.renren.constant.BizSystemException;
import io.renren.membermanage.bo.ModelTrainParamBo;
import io.renren.membermanage.entity.ModelTrain;
import io.renren.membermanage.service.DownloadFileService;
import io.renren.membermanage.service.ModelTrainService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

@Slf4j
@Service
public class DownloadFileServiceImpl implements DownloadFileService {

    @Value("${train.files.save.url}")
    private String baseDir;

    @Resource
    private ModelTrainService modelTrainService;

    @Override
    public void zipFilesAndDownload(List<Path> filePaths, HttpServletResponse response) throws IOException{
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=images.zip");
        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            for (Path filePath : filePaths) {
                File file = filePath.toFile();
                try (FileInputStream fis = new FileInputStream(file)) {
                    ZipEntry zipEntry = new ZipEntry(file.getName());
                    zipOut.putNextEntry(zipEntry);
                    byte[] bytes = new byte[1024];
                    int length;
                    while ((length = fis.read(bytes)) >= 0) {
                        zipOut.write(bytes, 0, length);
                    }
                }
                zipOut.closeEntry();
            }
        }
    }

    @Override
    public Map<String, String> uploadFiles(List<MultipartFile> files, Long userId) throws IOException{
        String workDic = System.getProperty("user.dir");
        workDic = workDic.substring(0,workDic.length()-1);
        workDic = workDic.substring(0,workDic.lastIndexOf("\\"));
        String uploadDir = workDic + baseDir + userId;
        // 校验文件
        Map<String, String> errors = validateFiles(files);
        // 如果有校验失败的文件，则返回错误信息
        if (!errors.isEmpty()) {
            return errors;
        }
        // 创建文件夹并存储文件
        String folderPath = Paths.get(uploadDir).toString();
        File folder = new File(folderPath);
        LambdaQueryWrapper<ModelTrain> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ModelTrain::getUserId, userId);
        wrapper.in(ModelTrain::getStatus, 2, 3);
        List<ModelTrain> list = modelTrainService.list(wrapper);
        if (CollUtil.isNotEmpty(list)) {
            throw new BizSystemException(500, "您当前存在正在训练的模型，或处在等待训练的模型。不允许上传！");
        }
        // 检查文件夹是否存在
        if (folder.exists()) {
            // 如果存在，则删除文件夹及其内容
            boolean flag = deleteRecursively(folder);
            if (!flag) {
                throw new BizSystemException(500, "文件夹删除失败！");
            }
        }
        // 尝试创建文件夹
        if (!folder.mkdirs()) {
            throw new BizSystemException(500, "文件夹创建失败！");
        }
        for (MultipartFile file : files) {
            try {
                String fileName = file.getOriginalFilename();
                if (StrUtil.isBlank(fileName)){
                    throw new BizSystemException(500, "您上传的文件中有文件名为空的文件请检查！");
                }
                File destFile = new File(folder, fileName);
                file.transferTo(destFile);
            } catch (IOException e) {
                errors.put("error", "文件保存失败");
                return errors;
            }
        }
        // 返回文件夹路径
        return Collections.singletonMap("uploadDir", uploadDir);
    }

    // 递归删除文件夹及其内容
    private boolean deleteRecursively(File folder) {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 如果是文件夹，递归删除
                    deleteRecursively(file);
                } else {
                    // 如果是文件，直接删除
                    file.delete();
                }
            }
        }
        // 最后删除空文件夹本身
        return folder.delete();
    }

    private Map<String, String> validateFiles(List<MultipartFile> files) throws IOException {
        Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("jpg", "png", "txt"));
        int MAX_IMAGE_SIZE = 2048;
        Map<String, String> errors = new HashMap<>();
        Set<String> jpgPngNames = new HashSet<>();
        Set<String> txtNames = new HashSet<>();

        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            if (StrUtil.isBlank(fileName)){
                throw new BizSystemException(500, "您上传的文件中有文件名为空的文件请检查！");
            }
            String extension = getFileExtension(fileName);
            if (!ALLOWED_EXTENSIONS.contains(extension)) {
                errors.put(fileName, "文件格式不正确");
                continue;
            }
            if ("jpg".equalsIgnoreCase(extension) || "png".equalsIgnoreCase(extension)) {
                BufferedImage image = ImageIO.read(file.getInputStream());
                if (image == null) {
                    errors.put(fileName, "无效的图片");
                    continue;
                }
                int width = image.getWidth();
                int height = image.getHeight();
                if (width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE) {
                    errors.put(fileName, "图片大小不是2048x2048");
                } else if (width % 64 != 0 || height % 64 != 0) {
                    errors.put(fileName, "图片像素必须是64的倍数");
                } else if (jpgPngNames.contains(getFileNameWithoutExtension(fileName))) {
                    errors.put(fileName, "存在同名的jpg/png");
                } else {
                    boolean hasTxt = false;
                    for (MultipartFile checkFile : files) {
                        if (getFileNameWithoutExtension(checkFile.getOriginalFilename()).equals(getFileNameWithoutExtension(fileName)) && "txt".equalsIgnoreCase(getFileExtension(checkFile.getOriginalFilename()))) {
                            hasTxt = true;
                            break;
                        }
                    }
                    if (!hasTxt) {
                        errors.put(fileName, "jpg/png必须和txt文件对应");
                        continue;
                    }
                    jpgPngNames.add(getFileNameWithoutExtension(fileName));
                }
            } else if ("txt".equalsIgnoreCase(extension)) {
                if (txtNames.contains(getFileNameWithoutExtension(fileName))) {
                    errors.put(fileName, "存在重复的txt");
                } else {
                    txtNames.add(getFileNameWithoutExtension(fileName));
                }
            }
        }
        return errors;
    }

    private String getFileExtension(String fileName) {
        return Optional.of(fileName.lastIndexOf("."))
                .map(i -> fileName.substring(i + 1).toLowerCase())
                .orElse("");
    }

    private String getFileNameWithoutExtension(String fileName) {
        return Optional.of(fileName.lastIndexOf("."))
                .map(i -> fileName.substring(0, i))
                .orElse(fileName);
    }
}
